/*
 * TMap Designer - A Thematic Map design software
 * Copyright (C) 2006 World Health Organization
 * http://www.who.int
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package who.tmap.toolkit.shape.impl;

import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Line2D;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.List;

import org.apache.log4j.Logger;

import who.tmap.common.TMapException;
import who.tmap.toolkit.shape.LabelShape;
import who.tmap.toolkit.shape.PolygonShape;
import who.tmap.toolkit.shape.PolygonShapeDisputedBorder;

/**
 * @author stl
 *
 * To change the template for this generated type comment go to
 * Window&gt;Preferences&gt;Java&gt;Code Generation&gt;Code and Comments
 */
public class PolygonShapeImpl extends BaseMapShapeImpl implements PolygonShape {

    private static Logger logger = Logger.getLogger(PolygonShapeImpl.class);      
    
	private GeneralPath generalPath =new GeneralPath();
	private List<PolygonShapeDisputedBorder> disputedBorders =new ArrayList<PolygonShapeDisputedBorder>();
	
	private String name;
	private LabelShape labelShape;
	boolean segmentsVisible =true;
	private String labelText;
	private Rectangle2D psSymbolBounds =null;
	
	PolygonShapeImpl() {
		setType( POLYGON );
	}
	
	private String cloneString( String str ) {
		return str != null ? new String( str ) : null;
	}
	
	/**
	 * @see UndoManager
	 */
    public Object deepClone() throws CloneNotSupportedException {
    	PolygonShapeImpl psi =new PolygonShapeImpl();
    	
    	psi.setId( cloneString( getId() ) );
    	psi.setType( getType() );
    	psi.setLayer( cloneString( getLayer() ) );
    	psi.select( isSelected() );
    	psi.setSelectedHandleIndex( getSelectedHandleIndex() );
    	psi.setColor( getColor() );
    	psi.setFillColor( getFillColor() );
    	
    	psi.bounds =(Rectangle2D)bounds.clone();
    	psi.setZoomFactor( getZoomFactor() );
    	psi.setMapFactor( getMapFactor() );
    	psi.setEditMode( getEditMode() );
    	
    	psi.generalPath =(GeneralPath)generalPath.clone();
    	psi.setName( cloneString( name ) );

    	psi.setLabelShape( (LabelShape)labelShape.deepClone() );
    	psi.getLabelShape().setMapShape( psi );
    	
    	psi.segmentsVisible =segmentsVisible;
    	psi.setLabelText( cloneString( labelText ) );

    	psi.disputedBorders =new ArrayList<PolygonShapeDisputedBorder>();
    	for( PolygonShapeDisputedBorder db : disputedBorders ) {
    		PolygonShapeDisputedBorder psdb = new PolygonShapeDisputedBorderImpl();
    		psi.disputedBorders.add( psdb );
    		psdb.setLine( (Line2D)db.getLine().clone() );
    		psdb.setStroke( db.getStroke() );
    	}
    	
    	return psi;
    }

	public void addPoint(Point2D pt) {
		if ( generalPath.getCurrentPoint() == null ) {
			generalPath.moveTo( (float)pt.getX(), (float)pt.getY() );
		} else {
			generalPath.lineTo( (float)pt.getX(), (float)pt.getY() );
		}
	}
	
	public void moveTo( Point2D pt ) {
		generalPath.moveTo( (float)pt.getX(), (float)pt.getY() );	    
	}
	
	public void lineTo( Point2D pt ) {
	    addPoint( pt );
	}
	
	public void close() {
		generalPath.closePath();
	}

	public void draw(Graphics g) {
		Color savColor =g.getColor();
		
		g.setColor( getColor() );		

		if ( disputedBorders.size() == 0 ) {
			if ( isSelected() || isSegmentsVisible() ) {
				((Graphics2D)g).draw( generalPath );
			}
		} else {
			drawGeneralPathWithDisputedBorders( ((Graphics2D)g) );
		}

		if ( isSelected() ) {
			int type;
			float[] c =new float[6];
			PathIterator it =generalPath.getPathIterator(null);
			while( !it.isDone() ) {
				type = it.currentSegment(c);  
				switch (type) {
					case PathIterator.SEG_MOVETO:
					case PathIterator.SEG_LINETO: 
						displayHandleRectangle( ((Graphics2D)g), c[0], c[1] );
					
						break;
				} 
				it.next();
			}
		}	
		
		bounds =generalPath.getBounds2D();
		
		//logger.debug( "polygon bounds= x=" + bounds.getMinX() + ", y=" + bounds.getMinY() + ", w=" + bounds.getWidth() + ", h=" + bounds.getHeight() );
		
		fill( g );
		
		g.setColor( savColor );
	}
	
	private void drawGeneralPathWithDisputedBorders( Graphics2D g ) {
		int type;
		float[] c =new float[6];
		float x, y, prevX =Float.MIN_VALUE, prevY =Float.MIN_VALUE, 
			  lastMoveToX =Float.MIN_VALUE, lastMoveToY =Float.MIN_VALUE;
		
		BasicStroke currentStroke =(BasicStroke)g.getStroke();

		float dash=(float)((getZoomFactor()/getMapFactor())/(getZoomFactor()/10f));
		float[] dashPhase =new float[] {dash/3.6f,dash/3.3f};
		
		BasicStroke dashStroke = new BasicStroke(
                currentStroke.getLineWidth(), currentStroke.getEndCap(), currentStroke.getLineJoin(),
                currentStroke.getMiterLimit(), dashPhase, 0 );		
		
		PathIterator it =generalPath.getPathIterator(null);
		while( !it.isDone() ) {
			type = it.currentSegment(c);
			
			switch (type) {
				case PathIterator.SEG_MOVETO:
					lastMoveToX =c[0];
					lastMoveToY =c[1];
					break;
					
				case PathIterator.SEG_LINETO:
				case PathIterator.SEG_CLOSE:					
					
					Line2D line2D =null;
					if ( type == PathIterator.SEG_LINETO ) {
						line2D =new Line2D.Float( prevX, prevY, c[0], c[1] );
					} else if ( type == PathIterator.SEG_CLOSE ) {
						line2D =new Line2D.Float( lastMoveToX, lastMoveToY, c[0], c[1] );						
					}
					
					boolean disputedBorder =false;
					for( PolygonShapeDisputedBorder db : disputedBorders ) {
						if ( db.getLine().getBounds2D().equals( line2D.getBounds2D() ) ) {
							disputedBorder =true;
							// Because of stroke issue, disputed borders need to be drawn 
							// in the same direction !
							line2D =db.getLine();
							break;
						}
					}
					
					if ( disputedBorder ) {
						g.setStroke( dashStroke );
					} else {
						g.setStroke( currentStroke );
					}
					
					if ( isSelected() || isSegmentsVisible() ) {
						g.draw( line2D );
					}
				
					break;

			} 
			
			it.next();
			
			prevX =c[0];
			prevY =c[1];
		}
		
		g.setStroke( currentStroke );
	}
	
	public void fill( Graphics g ) {
		fill( g, getFillColor() );
	}
	
	public void fill( Graphics g, Color color ) {
		if ( color != null ) {
			g.setColor( color );		
			((Graphics2D)g).fill( generalPath );
		}		
	}	
		
	public boolean contains( Point2D p ) {
		return generalPath.contains( p );
	}
	
	public boolean isInside( Rectangle2D r ) {
		return r.contains( generalPath.getBounds2D() );	
	}
	
	public Point2D getExactPointAtHandle( int handle ) {
	    Point2D exactPt =null;
		int ct =0;
		
		int type;
		float[] c =new float[6];
	
		PathIterator it =generalPath.getPathIterator(null);
		while( !it.isDone() && exactPt == null ) {
			type = it.currentSegment(c);
			
			if ( ct == handle && ( type == PathIterator.SEG_MOVETO || type == PathIterator.SEG_LINETO ) ) {
			    exactPt =new Point2D.Double( c[0], c[1] );
			    logger.debug( "exact point found x=" + exactPt.getX() + ", " + exactPt.getY() );
			    break;
			}

			ct++;
			
			it.next();
		}		
		
		return exactPt;
	}

	public int getHandleIndexAt( Point2D p ) {
		int handle =-1;
		int ct =0;
		
		int type;
		float[] c =new float[6];
	
		PathIterator it =generalPath.getPathIterator(null);
		while( !it.isDone() && handle == -1 ) {
			type = it.currentSegment(c);  
			switch (type) {
				case PathIterator.SEG_MOVETO:
				case PathIterator.SEG_LINETO: 
					if ( getHandleRectangle( c[0], c[1] ).contains( p ) ) {
						handle =ct;			
		
						logger.debug( "handle found=" + handle );
						break;
					}					
					break;	
			} 

			ct++;
			
			it.next();
		}		
		
		return handle;
	}
	
	public boolean containsEdge( Point2D p ) {
		boolean containsEdge =false;
		
		int type;
		float[] c =new float[6];	 
      
		PathIterator it =generalPath.getPathIterator(null);
		while( !it.isDone() ) {
			type = it.currentSegment(c); 
			
			if ( c[0] == p.getX() || c[1] == p.getY() ) {
				containsEdge =true;
				break;
			}
			
			it.next();
		}
		
		return containsEdge;
	}
	
	public void removePointAt( Point2D p ) {
		int ct =0;
		
		int type;
		float[] c =new float[6];	 
		GeneralPath newPath =new GeneralPath();	        
		PathIterator it =generalPath.getPathIterator(null);
		while( !it.isDone() ) {
			type = it.currentSegment(c); 
			
			if ( c[0] != p.getX() || c[1] != p.getY() || type == PathIterator.SEG_CLOSE ) {
				switch (type) {
				
					case PathIterator.SEG_MOVETO:
					    newPath.moveTo( c[0], c[1] );
					    break;
					    
					case PathIterator.SEG_LINETO: 
					    if ( newPath.getCurrentPoint() != null ) {
					        newPath.lineTo( c[0], c[1] );
					    } else {
					        newPath.moveTo( c[0], c[1] );
					    }
						break;
						
					case PathIterator.SEG_CLOSE:
					    if ( newPath.getCurrentPoint() != null ) {
					        newPath.closePath();
					    }
					    break;
				} 
			}

			ct++;
			
			it.next();
		}		
		
		if ( disputedBorders.size() > 0 ) {
			// Delete every disputed border segment containing the deleted point
			List<PolygonShapeDisputedBorder> toDelete =new ArrayList<PolygonShapeDisputedBorder>();
			for( PolygonShapeDisputedBorder db : disputedBorders ) {
				Line2D line2d =db.getLine();
				if ( db.getLine().getP1().equals( p ) || db.getLine().getP2().equals( p ) ) {
					toDelete.add( db );				
				}
			}	
			
			disputedBorders.removeAll( toDelete );
		}
		
		generalPath =newPath;
	}
	
	public Point2D insertPoint( Point2D p, boolean exactPt ) {
	    Point2D p1 =null, p2 =null, lastMoveTo =null, sdP1 =null, sdP2 =null;
	    double shortestDist =Double.MAX_VALUE;
	    
		int ct =0, sdCt =-1;
		
		int type;
		float[] c =new float[6];
		
		Rectangle2D handleRect =getHandleRectangle( p.getX(), p.getY() );
	
		PathIterator it =generalPath.getPathIterator(null);
		while( !it.isDone() ) {
			type = it.currentSegment(c);  
			switch (type) {
			
				case PathIterator.SEG_MOVETO:
				    p1 =new Point2D.Double( c[0], c[1] );
					p2 =null;
					lastMoveTo =new Point2D.Double( c[0], c[1] );
				    break;
				
				case PathIterator.SEG_LINETO:
				    p1 =p2 == null ? p1 : new Point2D.Double( p2.getX(), p2.getY() );
					p2 =new Point2D.Double( c[0], c[1] );
					break;
					
				case PathIterator.SEG_CLOSE:
				    p1 =new Point2D.Double( c[0], c[1] );
					p2 =new Point2D.Double( lastMoveTo.getX(), lastMoveTo.getY() );
				    break;
			} 
			
			if ( p1 != null &&  p2 != null ) {
			    if ( new Line2D.Double( p1.getX(), p1.getY(), p2.getX(), p2.getY() ).intersects( handleRect ) ) {
				    double dist =Line2D.ptSegDist( p1.getX(), p1.getY(), p2.getX(), p2.getY(), p.getX(), p.getY() );
				    if ( dist < shortestDist ) {
				        shortestDist =dist;
				        sdP1 =new Point2D.Double( p1.getX(), p1.getY() );
				        sdP2 =new Point2D.Double( p2.getX(), p2.getY() );
				        sdCt =ct;
				    }
			    }
			}
			    
			ct++;
			
			it.next();
		}		
		
	    if ( shortestDist != Double.MAX_VALUE ) {
	        
	        logger.debug( "px=" + p.getX() + ", py=" + p.getY() );
	        logger.debug( "sdP1x=" + sdP1.getX() + ", sdP1y=" + sdP1.getY() );
	        logger.debug( "sdP2x=" + sdP2.getX() + ", sdP2y=" + sdP2.getY() );
	        
	        if ( !exactPt ) {
	        
		        // Recompute p coordinates to be on the segment
		        // y =ax + b
		        if ( sdP1.getX() == sdP2.getX() ) {
		            p.setLocation( sdP1.getX(), p.getY() );
		        } else {
		            double a =( sdP2.getY() - sdP1.getY() ) / ( sdP2.getX() - sdP1.getX() );
		        	double b =sdP2.getY() - ( a * sdP2.getX() );
		        	
		        	logger.debug( "a=" + a + ", b=" + b );
			        
		        	double newY =(a * p.getX() ) + b;
				    if ( new Line2D.Double( sdP1.getX(), sdP1.getY(), sdP2.getX(), sdP2.getY() ).ptSegDist( p.getX(), newY ) == 0 ) {
				        p.setLocation( p.getX(), newY );
				    } else {
				        double newX =( p.getY() - b ) / a;
					    if ( new Line2D.Double( sdP1.getX(), sdP1.getY(), sdP2.getX(), sdP2.getY() ).ptSegDist( newX, p.getY() ) == 0 ) {
					        p.setLocation( newX, p.getY() );
					    }
				    }
            
		        }
	        }
	        
	        logger.debug( "pAfterx=" + p.getX() + ", pAftery=" + p.getY() );	        
	        
	        GeneralPath genPath =new GeneralPath();
			ct =0;
			
			it =generalPath.getPathIterator(null);
			while( !it.isDone() ) {

				type = it.currentSegment(c);
				
				if ( ct == sdCt ) {
				    if ( genPath.getCurrentPoint() != null ) {
				        genPath.lineTo( (float)p.getX(), (float)p.getY() );
				    } else {
				        genPath.moveTo( (float)p.getX(), (float)p.getY() );
				     
				    }
				}
							  
				switch (type) {
					case PathIterator.SEG_MOVETO:
						genPath.moveTo( c[0], c[1] );
						break;
						
					case PathIterator.SEG_LINETO: 
						genPath.lineTo( c[0], c[1] );	
						break;
							
					case PathIterator.SEG_CLOSE:
						genPath.closePath();
						break;				
				} 
				
				ct++;
				
				it.next();
			}		
			
			if ( disputedBorders.size() > 0 ) {
				List<PolygonShapeDisputedBorder> toDelete =new ArrayList<PolygonShapeDisputedBorder>();			
				for( PolygonShapeDisputedBorder db : disputedBorders ) {
					Line2D line2d =db.getLine();
					if ( ( db.getLine().getP1().equals( sdP1 ) && db.getLine().getP2().equals( sdP2 ) )
						|| ( db.getLine().getP1().equals( sdP2 ) && db.getLine().getP2().equals( sdP1 ) ) ) {
						toDelete.add( db );
						
						PolygonShapeDisputedBorder db2 =new PolygonShapeDisputedBorderImpl();
						db2.setLine( new Line2D.Float( sdP1, p ) );
						db2.setStroke( db.getStroke() );
						disputedBorders.add( db2 );
						
						db2 =new PolygonShapeDisputedBorderImpl();
						db2.setLine( new Line2D.Float( p, sdP2 ) );
						db2.setStroke( db.getStroke() );
						disputedBorders.add( db2 );						
					}
				}
				
				disputedBorders.removeAll( toDelete );
			}
			
			generalPath =genPath;
	    }
		
		return shortestDist != Double.MAX_VALUE ? p : null;
	}

	public void updateShape(int idx, Point2D p) {
		GeneralPath genPath =new GeneralPath();
		int type;
		int ct =0;
		float[] c =new float[6];		
		float x,y, updatePrevX =Float.MIN_VALUE, updatePrevY =Float.MIN_VALUE;
		
		PathIterator it =generalPath.getPathIterator(null);
		while( !it.isDone() ) {

			type = it.currentSegment(c);
			
			if ( ct == idx ) {
				x =(float)p.getX();
				y =(float)p.getY();
				
				updatePrevX =c[0];
				updatePrevY =c[1];
			} else {
				x =c[0];
				y =c[1]; 
			}
			  
			switch (type) {
				case PathIterator.SEG_MOVETO:
					genPath.moveTo( x, y );
					break;
					
				case PathIterator.SEG_LINETO: 
					genPath.lineTo( x, y );	
					break;
						
				case PathIterator.SEG_CLOSE:
					genPath.closePath();
					break;				
			} 
			
			ct++;
			
			it.next();
		}	
		
		if ( updatePrevX != Float.MIN_VALUE ) {
			for( PolygonShapeDisputedBorder db : disputedBorders ) {
				Line2D line2d =db.getLine();
				if ( db.getLine().getX1() == updatePrevX && db.getLine().getY1() == updatePrevY ) {
					line2d.setLine( p.getX(), p.getY(), line2d.getX2(), line2d.getY2() );
				} else if ( db.getLine().getX2() == updatePrevX && db.getLine().getY2() == updatePrevY ) {
					line2d.setLine( line2d.getX1(), line2d.getY1(), p.getX(), p.getY() );					
				}
			}		
		}
		
		generalPath =genPath;
		
		// for test purpose
		bounds =generalPath.getBounds2D();		
	}
	
	public void moveShape( Point2D anchor, Point2D movingAnchor ) {
		GeneralPath genPath =new GeneralPath();
		int type;
		int ct =0;
		float[] c =new float[6];		
		float x,y;
    
	    double offsetX =movingAnchor.getX() - anchor.getX();
	    double offsetY =movingAnchor.getY() - anchor.getY();    
		
		PathIterator it =generalPath.getPathIterator(null);
		while( !it.isDone() ) {

			type = it.currentSegment(c);
			
			x =c[0] + (float)offsetX;
			y =c[1] + (float)offsetY; 
			  
			switch (type) {
				case PathIterator.SEG_MOVETO:
					genPath.moveTo( x, y );
					break;
					
				case PathIterator.SEG_LINETO: 
					genPath.lineTo( x, y );	
					break;
						
				case PathIterator.SEG_CLOSE:
					genPath.closePath();
					break;				
			} 
			
			ct++;
			
			it.next();
		}		
		
		for( PolygonShapeDisputedBorder db : disputedBorders ) {
			Line2D line2d =db.getLine();
			line2d.setLine( line2d.getX1() + (float)offsetX, line2d.getY1() + (float)offsetY,
							line2d.getX2() + (float)offsetX, line2d.getY2() + (float)offsetY );					
		}
		
		generalPath =genPath;	    
	    
		bounds =generalPath.getBounds2D();
	}		
	
	public void movePoint( Point2D origPt, Point2D newPt ) {
		int ct =0;
		
		int type;
		float[] c =new float[6];	 
		float x,y;
		// First ensure that a handle has been clicked
	    int handle =getHandleIndexAt( origPt );    
	    
	    if ( handle != -1 ) {
	    	GeneralPath newPath =new GeneralPath();	        
			PathIterator it =generalPath.getPathIterator(null);
			while( !it.isDone() ) {
				type = it.currentSegment(c); 
				
				if ( (float)origPt.getX() == c[0] && (float)origPt.getY() == c[1] ) {
				    x =(float)newPt.getX();
				    y =(float)newPt.getY();
				} else {
				    x =c[0];
				    y =c[1];
				}
				switch (type) {
					
					case PathIterator.SEG_MOVETO:
					    newPath.moveTo( x, y );
					    break;
					    
					case PathIterator.SEG_LINETO: 
					    if ( newPath.getCurrentPoint() != null ) {
					        newPath.lineTo( x, y );
					    } else {
					        newPath.moveTo( x, y );
					    }
						break;
						
					case PathIterator.SEG_CLOSE:
					    if ( newPath.getCurrentPoint() != null ) {
					        newPath.closePath();
					    }
					    break;
				} 

				ct++;
				
				it.next();
			}		
			
			for( PolygonShapeDisputedBorder db : disputedBorders ) {
				Line2D line2d =db.getLine();
				if ( db.getLine().getX1() == origPt.getX() && db.getLine().getY1() == origPt.getY() ) {
					line2d.setLine( newPt, line2d.getP2() );
				} else if ( db.getLine().getX2() == origPt.getX() && db.getLine().getY2() == origPt.getY() ) {
					line2d.setLine( line2d.getP1(), newPt );					
				}
			}				
			
			generalPath =newPath;
	    }
	    
		// for test purpose
		bounds =generalPath.getBounds2D();
	}
	
	/**
	 * For DAO layer purpose
	 */
	public GeneralPath getGeneralPath() {
	    return generalPath;
	}
	
	public void setGeneralPath( GeneralPath generalPath ) {
		this.generalPath =generalPath;
	}
	
	public void merge( PolygonShape shapeToMerge ) throws TMapException {
		try {
            PolygonShapeImplMergeHelper psiHelper =new PolygonShapeImplMergeHelper( this, shapeToMerge );	    

            java.awt.geom.GeneralPath path =psiHelper.merge();
            if ( path != null ) {
                generalPath =path;
            } else {
                generalPath.append( shapeToMerge.getGeneralPath().getPathIterator(null), false );                
            }
            
        } catch (Exception e) {
            e.printStackTrace();
            throw new TMapException( e );
        }
	}
	
	public void addCommonBoundary( PolygonShape polygonShape, Point2D firstPt, Point2D secondPt, Point2D pathPt ) throws TMapException {
	    PolygonShapeImplCommonBoundaryHelper cbHelper 
	    						=new PolygonShapeImplCommonBoundaryHelper(
	            					this, polygonShape, 
	            					firstPt, secondPt, pathPt,
	            					getMapFactor(), getZoomFactor() );
	    cbHelper.addCommonBoundary();
	    
	}

	public PolygonShape[] split(Point2D firstPt, Point2D secondPt) throws TMapException {        
	    PolygonShapeImplSplitHelper helper =new PolygonShapeImplSplitHelper( this, firstPt, secondPt );
	    
	    return helper.split();
    }
	
    /**
     * @return Returns the name.
     */
    public String getName() {
        return name;
    }
    
    /**
     * @param name The name to set.
     */
    public void setName(String name) {
        this.name = name;
    }    
    
	public LabelShape getLabelShape() {
		return labelShape;
	}

	public void setLabelShape(LabelShape labelShape) {
		this.labelShape = labelShape;
	}

	public Point2D getCenter() {
		return new Point2D.Double( getBounds().getCenterX(), getBounds().getCenterY() );
	}	
		
	public boolean isSegmentsVisible() {
		return segmentsVisible;
	}

	public void setSegmentsVisible(boolean visible) {
		this.segmentsVisible = visible;
	}

	public List<PolygonShapeDisputedBorder> getDisputedBorders() {
		return disputedBorders;
	}

	public void setDisputedBorders(List<PolygonShapeDisputedBorder> disputedBorders) {
		this.disputedBorders = disputedBorders;
	}

	/**
     * Used for selecting polygons by name
     */
    public String toString() {
    	return name != null ? name : "";
    }
    
    public boolean addDisputedBorder( Point2D p, int strokeStyle ) {
	    Point2D p1 =null, p2 =null, lastMoveTo =null, sdP1 =null, sdP2 =null;
	    double shortestDist =Double.MAX_VALUE;
	    
		int ct =0, sdCt =-1;
		
		int type;
		float[] c =new float[6];
		
		Rectangle2D handleRect =getHandleRectangle( p.getX(), p.getY() );
	
		PathIterator it =generalPath.getPathIterator(null);
		while( !it.isDone() ) {
			type = it.currentSegment(c);  
			switch (type) {
			
				case PathIterator.SEG_MOVETO:
				    p1 =new Point2D.Double( c[0], c[1] );
					p2 =null;
					lastMoveTo =new Point2D.Double( c[0], c[1] );
				    break;
				
				case PathIterator.SEG_LINETO:
				    p1 =p2 == null ? p1 : new Point2D.Double( p2.getX(), p2.getY() );
					p2 =new Point2D.Double( c[0], c[1] );
					break;
					
				case PathIterator.SEG_CLOSE:
				    p1 =new Point2D.Double( c[0], c[1] );
					p2 =new Point2D.Double( lastMoveTo.getX(), lastMoveTo.getY() );
				    break;
			} 
			
			if ( p1 != null &&  p2 != null ) {
			    if ( new Line2D.Double( p1.getX(), p1.getY(), p2.getX(), p2.getY() ).intersects( handleRect ) ) {
				    double dist =Line2D.ptSegDist( p1.getX(), p1.getY(), p2.getX(), p2.getY(), p.getX(), p.getY() );
				    if ( dist < shortestDist ) {
				        shortestDist =dist;
				        sdP1 =new Point2D.Double( p1.getX(), p1.getY() );
				        sdP2 =new Point2D.Double( p2.getX(), p2.getY() );
				        sdCt =ct;
				    }
			    }
			}
			    
			ct++;
			
			it.next();
		}		
		
	    if ( shortestDist != Double.MAX_VALUE ) {
	    	PolygonShapeDisputedBorder psdb =new PolygonShapeDisputedBorderImpl();
	    	psdb.setLine( new Line2D.Float( sdP1, sdP2 ) );
	    	psdb.setStroke( strokeStyle );
	    	
			List<PolygonShapeDisputedBorder> toDelete =new ArrayList<PolygonShapeDisputedBorder>();	
	    	for( PolygonShapeDisputedBorder db : disputedBorders ) {
	    		if ( db.getLine().getBounds2D().equals( psdb.getLine().getBounds2D() ) ) {
	    			toDelete.add( db );
	    		}
	    	}
	    	
	    	if ( toDelete.size() == 0 ) {
	    		disputedBorders.add( psdb );
	    	} else {
	    		disputedBorders.removeAll( toDelete );
	    	}
	    }
	    
	    return ( shortestDist != Double.MAX_VALUE );
  	
    }

	public String getLabelText() {
		return labelText;
	}

	public void setLabelText(String labelText) {
		this.labelText = labelText;
	}

	public void setPsSymbolBounds(Rectangle2D psSymbolBounds) {
		this.psSymbolBounds = psSymbolBounds;
	}

	public Rectangle2D getPsSymbolBounds() {
		return psSymbolBounds;
	}
    	
}
